/*
 *   O         ,-
 *  ° o    . -´  '     ,-
 *   °  .´        ` . ´,´
 *     ( °   ))     . (
 *      `-;_    . -´ `.`.
 *          `._'       ´
 *
 * Copyright (c) 2006 Markus Fisch <mf@markusfisch.de>
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 */
#include "Compiler.h"

#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <string>

using namespace cpprc;
using namespace std;

/**
 * Clean up
 */
Compiler::~Compiler()
{
	flush();
}

/**
 * Add a file to resource
 *
 * @param f - path and file-name of file to add
 */
void Compiler::add( const char *f )
{
	ifstream in( f, ios::binary );

	if( !in ||
		!in.good() )
		throw "file not found !";

	in.seekg( 0, ios::end );
	int length = in.tellg();
	in.seekg( 0, ios::beg );

	if( !length )
		return;

	index << "// " << f << endl
		<< "\t{ " << hash( f ) << ", " << offset << ", " << length << " },"
		<< endl;

	if( data.tellp() > 0 )
		data << "," << endl;

	data << "// " << f << endl;

	for( int column = 0, c = 0; ;
		column += 6, ++offset )
	{
		c = in.get();

		if( in.eof() )
			break;

		if( column )
			data << ", ";

		if( column > 70 )
		{
			data << endl;
			column = 0;
		}

		data << "0x" << hex << c;
	}
}

/**
 * Return hash value for string
 *
 * @param s - some string
 */
int Compiler::hash( const char *s ) const
{
	int h;

	for( h = *(s++) % HashMax; *s; ++s )
		h = (h*128+*s) % HashMax;

	return h;
}

/**
 * Flush data to file
 */
void Compiler::flush()
{
	if( data.tellp() <= 0 )
		return;

	// .h file
	{
		ofstream out( (fileName+".h").c_str() );

		if( !out )
			throw "can not write output file !";

		switch( format )
		{
			default:
			case STL:
				out <<
"/*\n\
 * Warning: Do NOT edit this file !\n\
 *\n\
 * This file is genereated automatically ! All changes will be lost !\n\
 */\n\
\n\
#ifndef " << fileName << "_h\n\
#define " << fileName << "_h\n\
\n\
#include <istream>\n\
#include <string>\n\
\n\
/**\n\
 * Manage resources\n\
 */\n\
class " << fileName << "\n\
{\n\
	public:\n\
		" << fileName << "( const std::string & );\n\
		virtual ~" << fileName << "();\n\
		inline bool good() const { return in ? true : false; }\n\
		inline std::istream *stream() const { return in; }\n\
		static std::istream *get( const std::string & );\n\
		static std::string getString( const std::string & );\n\
\n\
	private:\n\
		std::istream *in;\n\
\n\
		static int hash( const char * );\n\
};\n\
\n\
#endif" << endl;
				break;
			case wxWidgets:
				out <<
"/*\n\
 * Warning: Do NOT edit this file !\n\
 *\n\
 * This file is genereated automatically ! All changes will be lost !\n\
 */\n\
\n\
#ifndef " << fileName << "_h\n\
#define " << fileName << "_h\n\
\n\
#include <wx/wx.h>\n\
#include <wx/stream.h>\n\
\n\
/**\n\
 * Manage resources\n\
 */\n\
class " << fileName << "\n\
{\n\
	public:\n\
		" << fileName << "( const wxString & );\n\
		virtual ~" << fileName << "();\n\
		inline bool IsOk() const { return in ? true : false; }\n\
		inline wxInputStream *GetStream() const { return in; }\n\
		static wxInputStream *Get( const wxString & );\n\
\n\
	private:\n\
		wxInputStream *in;\n\
\n\
		static int Hash( const char * );\n\
};\n\
\n\
#endif" << endl;
				break;
		}
	}

	// .c file
	{
		ofstream out( (fileName+".cpp").c_str() );

		if( !out )
			throw "can not write output file !";

		switch( format )
		{
			default:
			case STL:
				out <<
"/*\n\
 * Warning: Do NOT edit this file !\n\
 *\n\
 * This file is genereated automatically ! All changes will be lost !\n\
 */\n\
\n\
#include \"" << fileName << ".h\"\n\
\n\
#include <istream>\n\
#include <fstream>\n\
#include <sstream>\n\
#include <string>\n\
\n\
using namespace std;\n\
\n\
static const struct RESOURCE_INDEX\n\
{\n\
	const int hash;\n\
	const int offset;\n\
	const int length;\n\
} resources_index[] = {\n\
" << index.str() << "\
	{ 0, 0, 0 }\n\
};\n\
\n\
static const char resources_data[] = {\n\
	" << data.str() << "\n\
};\n\
\n\
/**\n\
 * Initialize object\n\
 *\n\
 * @param fileName - path and file name\n\
 */\n\
" << fileName << "::" << fileName << "( const string &fileName ) :\n\
	in( get( fileName ) )\n\
{\n\
}\n\
\n\
/**\n\
 * Clean up\n\
 */\n\
" << fileName << "::~" << fileName << "()\n\
{\n\
	if( !in )\n\
		return;\n\
\n\
	delete in;\n\
}\n\
\n\
/**\n\
 * Return input stream for a given file\n\
 *\n\
 * @param fileName - path and file name\n\
 */\n\
istream *Resource::get( const string &fileName )\n\
{\n\
	if( fileName[0] != ':' )\n\
		return new ifstream( fileName.c_str(), ios::binary );\n\
\n\
	int h = hash( fileName.substr( 1 ).c_str() );\n\
	const struct RESOURCE_INDEX *r = resources_index;\n\
\n\
	for( ; r->hash && r->hash != h; ++r );\n\
\n\
	if( !r->hash )\n\
		return 0;\n\
\n\
	string s( &resources_data[r->offset], r->length );\n\
\n\
	return new istringstream( s, ios::binary );\n\
}\n\
\n\
/**\n\
 * Return contents as a string of the given file\n\
 *\n\
 * @param fileName - path and file name\n\
 */\n\
string " << fileName << "::getString( const string &fileName  )\n\
{\n\
	" << fileName << " r( fileName );\n\
\n\
	if( !r.good() )\n\
		return \"\";\n\
\n\
	istream &in = *r.stream();\n\
\n\
	in.seekg( 0, ios::end );\n\
	int length = in.tellg();\n\
	in.seekg( 0, ios::beg );\n\
\n\
	char buffer[length+1];\n\
\n\
	in.read( buffer, length );\n\
	buffer[length] = 0;\n\
\n\
	return string( buffer );\n\
}\n\
\n\
/**\n\
 * Calculate hash value\n\
 *\n\
 * @param s - some string\n\
 */\n\
int " << fileName << "::hash( const char *s )\n\
{\n\
	int h;\n\
\n\
	for( h = *(s++) % " << HashMax << "; *s; ++s )\n\
		h = (h*128+*s) % " << HashMax << ";\n\
\n\
	return h;\n\
}" << endl;
				break;
			case wxWidgets:
				out <<
"/*\n\
 * Warning: Do NOT edit this file !\n\
 *\n\
 * This file is genereated automatically ! All changes will be lost !\n\
 */\n\
\n\
#include \"" << fileName << ".h\"\n\
\n\
#include <wx/wx.h>\n\
#include <wx/filename.h>\n\
#include <wx/stream.h>\n\
#include <wx/wfstream.h>\n\
#include <wx/mstream.h>\n\
\n\
static const struct RESOURCE_INDEX\n\
{\n\
\tconst int hash;\n\
\tconst int offset;\n\
\tconst int length;\n\
} resources_index[] = {\n\
" << index.str() << "\
	{ 0, 0, 0 }\n\
};\n\
\n\
static const unsigned char resources_data[] = {\n\
" << data.str() << "\
};\n\
\n\
/**\n\
 * Initialize object\n\
 *\n\
 * @param fileName - path and file name\n\
 */\n\
" << fileName << "::" << fileName << "( const wxString &fileName ) :\n\
	in( Get( fileName ) )\n\
{\n\
}\n\
\n\
/**\n\
 * Clean up\n\
 */\n\
" << fileName << "::~" << fileName << "()\n\
{\n\
	if( !in )\n\
		return;\n\
\n\
	delete in;\n\
}\n\
\n\
/**\n\
 * Return input stream for a given file\n\
 *\n\
 * @param fileName - path and file name\n\
 */\n\
wxInputStream *" << fileName << "::Get( const wxString &fileName )\n\
{\n\
	if( fileName[0] != ':' )\n\
		return new wxFileInputStream( fileName );\n\
\n\
	int hash = Hash( fileName.Mid( 1 ).mb_str( wxConvUTF8 ) );\n\
	const struct RESOURCE_INDEX *r = resources_index;\n\
\n\
	for( ; r->hash && r->hash != hash; ++r );\n\
\n\
	if( !r->hash )\n\
		return 0;\n\
\n\
	return new wxMemoryInputStream( &resources_data[r->offset],\n\
		r->length );\n\
}\n\
\n\
/**\n\
 * Calculate hash value\n\
 *\n\
 * @param s - some string\n\
 */\n\
int " << fileName << "::Hash( const char *s )\n\
{\n\
	int h;\n\
\n\
	for( h = *(s++) % " << HashMax << "; *s; ++s )\n\
		h = (h*128+*s) % " << HashMax << ";\n\
\n\
	return h;\n\
}" << endl;
				break;
		}
	}

	data.clear();
	index.clear();
	offset = 0;
}
